package com.ouc.openplatform.pampas;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ouc.openplatform.annotation.Export;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.convert.support.DefaultConversionService;

public class ParamUtil
{
  private static final Logger log = LoggerFactory.getLogger(ParamUtil.class);
  private static ObjectMapper mapper;
  private static JavaType mapType;
  private static Map<String, Class<?>> primitiveClassMap = new HashMap();
  private static DefaultConversionService conversionService = new DefaultConversionService();
  
  static
  {
    mapper = new ObjectMapper();
    
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    
    mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    mapType = mapper.getTypeFactory().constructParametricType(HashMap.class, new Class[] { String.class, Object.class });
    
    primitiveClassMap.put("int", Integer.TYPE);
    primitiveClassMap.put("long", Long.TYPE);
    primitiveClassMap.put("short", Short.TYPE);
    primitiveClassMap.put("byte", Byte.TYPE);
    primitiveClassMap.put("char", Character.TYPE);
    primitiveClassMap.put("boolean", Boolean.TYPE);
    primitiveClassMap.put("float", Float.TYPE);
    primitiveClassMap.put("double", Double.TYPE);
  }
  
  public static Class<?> getPrimitiveClass(String className)
  {
    return (Class)primitiveClassMap.get(className);
  }
  
  public static boolean isPrimitive(Object obj)
  {
    if ((obj instanceof Class)) {
      return (((Class)obj).isPrimitive()) || (obj == String.class) || (obj == Integer.class) || (obj == Long.class) || (obj == Short.class) || (obj == Byte.class) || (obj == Character.class) || (obj == Boolean.class) || (obj == Float.class) || (obj == Double.class);
    }
    return ((obj instanceof String)) || ((obj instanceof Integer)) || ((obj instanceof Long)) || ((obj instanceof Short)) || ((obj instanceof Byte)) || ((obj instanceof Character)) || ((obj instanceof Boolean)) || ((obj instanceof Float)) || ((obj instanceof Double));
  }
  
  public static Object convert(Object param, Class clazz, JavaType javaType, Map<String, Object> context)
  {
    if (isPrimitive(clazz)) {
      return conversionService.convert(param, clazz);
    }
    if ((param != null) && (clazz.isAssignableFrom(param.getClass()))) {
      return param;
    }
    if ((param != null) && ((param instanceof String))) {
      try
      {
        if (javaType != null) {
          return mapper.readValue(param.toString(), javaType);
        }
        return mapper.readValue(param.toString(), clazz);
      }
      catch (IOException e)
      {
        throw new RuntimeException("json 2 obj mapping error: " + param, e);
      }
    }
    if ((context == null) || (context.isEmpty())) {
      return null;
    }
    return mapper.convertValue(context, clazz);
  }
  
  public static Object convertResult(Object result)
  {
    if (result == null) {
      return null;
    }
    if (isPrimitive(result)) {
      return result;
    }
    Map convertedResult = (Map)mapper.convertValue(new ConvertWrap(result), mapType);
    return convertedResult.get("obj");
  }
  
  public static MethodInfo getMethodInfo(Object bean, Method method)
  {
    Export methodExport = (Export)method.getAnnotation(Export.class);
    if (methodExport == null) {
      return null;
    }
    MethodInfo methodInfo = new MethodInfo();
    methodInfo.bean = bean;
    methodInfo.method = method;
    Class<?>[] paramClasses = method.getParameterTypes();
    Type[] paramTypes = method.getGenericParameterTypes();
    String[] paramNames = methodExport.paramNames();
    if (paramClasses.length != paramNames.length) {
      throw new IllegalArgumentException("param count not same: " + method.toString());
    }
    for (int i = 0; i < paramClasses.length; i++)
    {
      JavaType javaType = constructJavaType(paramTypes[i]);
      methodInfo.params.put(paramNames[i], new ParamInfo(paramClasses[i], javaType));
    }
    return methodInfo;
  }
  
  private static JavaType constructJavaType(Type type)
  {
    if ((type instanceof ParameterizedType))
    {
      ParameterizedType ptype = (ParameterizedType)type;
      Type[] typeArgs = ptype.getActualTypeArguments();
      JavaType[] javaTypes = new JavaType[typeArgs.length];
      for (int i = 0; i < typeArgs.length; i++) {
        javaTypes[i] = constructJavaType(typeArgs[i]);
      }
      return mapper.getTypeFactory().constructParametricType((Class)ptype.getRawType(), javaTypes);
    }
    return mapper.getTypeFactory().constructType(type);
  }
  
  public static class MethodInfo
  {
    private Object bean;
    private Method method;
    
    public Object getBean()
    {
      return this.bean;
    }
    
    public void setBean(Object bean)
    {
      this.bean = bean;
    }
    
    public Method getMethod()
    {
      return this.method;
    }
    
    public void setMethod(Method method)
    {
      this.method = method;
    }
    
    public LinkedHashMap<String, ParamUtil.ParamInfo> getParams()
    {
      return this.params;
    }
    
    private LinkedHashMap<String, ParamUtil.ParamInfo> params = new LinkedHashMap();
    
    public void setParams(LinkedHashMap<String, ParamUtil.ParamInfo> params)
    {
      this.params = params;
    }
  }
  
  public static class ParamInfo
  {
    private Class<?> clazz;
    private JavaType javaType;
    
    public Class<?> getClazz()
    {
      return this.clazz;
    }
    
    public void setClazz(Class<?> clazz)
    {
      this.clazz = clazz;
    }
    
    public JavaType getJavaType()
    {
      return this.javaType;
    }
    
    public void setJavaType(JavaType javaType)
    {
      this.javaType = javaType;
    }
    
    public ParamInfo(Class<?> clazz, JavaType javaType)
    {
      this.clazz = clazz;
      this.javaType = javaType;
    }
  }
  
  private static class ConvertWrap implements Serializable
  {
    private static final long serialVersionUID = -4890673040481403994L;
    private Object obj;
    
    public Object getObj()
    {
      return this.obj;
    }
    
    public void setObj(Object obj)
    {
      this.obj = obj;
    }
    
    private ConvertWrap(Object obj)
    {
      this.obj = obj;
    }
  }
}
